; vim: ts=4:et:sw=4:
; Copyleft (K) by Jose M. Rodriguez de la Rosa
;  (a.k.a. Boriel)
;  http://www.boriel.com
;
; This ASM library is licensed under the BSD license
; you can use it for any purpose (even for commercial
; closed source programs).
;
; Please read the BSD license on the internet

; ----- IMPLEMENTATION NOTES ------
; The heap is implemented as a linked list of free blocks.

; Each free block contains this info:
;
; +----------------+ <-- HEAP START
; | Size (2 bytes) |
; |        0       | <-- Size = 0 => DUMMY HEADER BLOCK
; +----------------+
; | Next (2 bytes) |---+
; +----------------+ <-+
; | Size (2 bytes) |
; +----------------+
; | Next (2 bytes) |---+
; +----------------+   |
; | <free bytes...>|   | <-- If Size > 4, then this contains (size - 4) bytes
; | (0 if Size = 4)|   |
; +----------------+ <-+
; | Size (2 bytes) |
; +----------------+
; | Next (2 bytes) |---+
; +----------------+   |
; | <free bytes...>|   |
; | (0 if Size = 4)|   |
; +----------------+   |
;   <Allocated>        | <-- This zone is in use (Already allocated)
; +----------------+ <-+
; | Size (2 bytes) |
; +----------------+
; | Next (2 bytes) |---+
; +----------------+   |
; | <free bytes...>|   |
; | (0 if Size = 4)|   |
; +----------------+ <-+
; | Next (2 bytes) |--> NULL => END OF LIST
; |    0 = NULL    |
; +----------------+
; | <free bytes...>|
; | (0 if Size = 4)|
; +----------------+


; When a block is FREED, the previous and next pointers are examined to see
; if we can defragment the heap. If the block to be breed is just next to the
; previous, or to the next (or both) they will be converted into a single
; block (so defragmented).


;   MEMORY MANAGER
;
; This library must be initialized calling __MEM_INIT with
; HL = BLOCK Start & DE = Length.

; An init directive is useful for initialization routines.
; They will be added automatically if needed.


#include once <error.asm>
#include once <alloc.asm>
#include once <free.asm>


; ---------------------------------------------------------------------
; MEM_REALLOC
;  Reallocates a block of memory in the heap.
;
; Parameters
;  HL = Pointer to the original block
;  BC = New Length of requested memory block
;
; Returns:
;  HL = Pointer to the allocated block in memory. Returns 0 (NULL)
;       if the block could not be allocated (out of memory)
;
; Notes:
;  If BC = 0, the block is freed, otherwise
;  the content of the original block is copied to the new one, and
;  the new size is adjusted. If BC < original length, the content
;  will be truncated. Otherwise, extra block content might contain
;  memory garbage.
;
; ---------------------------------------------------------------------
__REALLOC:
; Reallocates block pointed by HL, with new length BC
                     ;- PROC
                     ;- LOCAL __REALLOC_END
lda z80_h            ;- ld a,h
sta z80_a
ora z80_l            ;- or l
jeq __MEM_ALLOC      ;- jp z, __MEM_ALLOC    ; If HL == NULL, just do a malloc
ldy #$00             ;- ld e,(hl)
lda (z80_hl),y
sta z80_e
inc z80_l            ;- inc hl
bne *+4
inc z80_h
ldy #$00             ;- ld d, (hl)    ; DE = First 2 bytes of HL block
lda (z80_hl),y
sta z80_d
lda z80_l            ;- push hl
pha
lda z80_h
pha
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
pla            ;- pop de
sta z80_d
pla
sta z80_e
inc z80_e      ;- inc de        ; DE' <- HL + 2
bne *+4
inc z80_d
lda z80_c      ;- exx            ; DE' <- HL (Saves current pointer into DE')
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- dec hl        ; HL = Block start
lda z80_e            ;- push de
pha
lda z80_d
pha
lda z80_c            ;- push bc
pha
lda z80_b
pha
jsr __MEM_FREE       ;- call __MEM_FREE        ; Frees current block
pla                  ;- pop bc
sta z80_b
pla
sta z80_c
lda z80_c            ;- push bc
pha
lda z80_b
pha
jsr __MEM_ALLOC      ;- call __MEM_ALLOC    ; Gets a new block of length BC
pla                  ;- pop bc
sta z80_b
pla
sta z80_c
pla                  ;- pop de
sta z80_d
pla
sta z80_e
lda z80_h            ;- ld a,h
sta z80_a
ora z80_l            ;- or l
                     ;- ret z        ; Return if HL == NULL (No memory)
lda z80_e            ;- ld (hl),e
ldy #$00
sta (z80_hl),y
inc z80_l            ;- inc hl
bne *+4
inc z80_h
lda z80_d            ;- ld (hl),d
ldy #$00
sta (z80_hl),y
inc z80_l            ;- inc hl        ; Recovers first 2 bytes in HL
bne *+4
inc z80_h
                     ;- dec bc
                     ;- dec bc        ; BC = BC - 2 (Two bytes copied)
lda z80_b            ;- ld a,b
sta z80_a
ora z80_c            ;- or c
                     ;- jp z, __REALLOC_END        ; Ret if nothing to copy (BC == 0)
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
lda z80_e            ;- push de
pha
lda z80_d
pha
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
pla                  ;- pop de        ; DE <- DE' ; Start of remaining block
sta z80_d
pla
sta z80_e
lda z80_l            ;- push hl        ; Saves current Block + 2 start
pha
lda z80_h
pha
lda z80_e            ;- ex de,hl    ; Exchanges them: DE is destiny block
ldx z80_l
stx z80_e
sta z80_l
lda z80_d
ldx z80_h
stx z80_d
sta z80_h
                     ;- ldir        ; Copies BC Bytes
pla                  ;- pop hl        ; Recovers Block + 2 start
sta z80_h
pla
sta z80_l

__REALLOC_END:
                     ;- dec hl        ; Set HL
                     ;- dec hl        ; To begin of block
rts                  ;- ret
                     ;- ENDP

